package com.terry.proxy.socket.impl;

import com.terry.proxy.application.cache.DBCache;
import com.terry.proxy.common.Msg;
import com.terry.proxy.socket.ISocketProxyClient;
import com.terry.proxy.util.SocketCreateUtil;
import com.terry.proxy.util.TcpConsumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * netty socket 代理客户端
 *
 * @author terry
 * @version 1.0
 * @date 2022/12/14 16:11
 */
@Slf4j
@ConditionalOnProperty(name = "proxy.protocol", havingValue = "netty-client")
@Component
public class NettySocketProxyClient extends ISocketProxyClient {

    @Resource
    private DBCache dbCache;

    private String serverIp;

    private Integer serverPort;

    // 断线重连
    public static final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor();

    // 心跳重连
    public static final ScheduledExecutorService heartbeatExecutor = Executors.newSingleThreadScheduledExecutor();

    @Override
    @EventListener(ApplicationReadyEvent.class)
    public void initConfig() {
        // 初始化DB
        dbCache.initSysConfig();
        this.serverIp = DBCache.getConfigByKey(DBCache.CONFIG_SERVER_IP);
        this.serverPort = Integer.parseInt(DBCache.getConfigByKey(DBCache.CONFIG_SERVER_PORT));
        log.info(" 服务器IP端口 {}:{},客户端ID {}", serverIp, serverPort, clientId);
        // 断线重连
        reconnectExecutor.scheduleAtFixedRate(() -> {
            try {
                if (serverChannel == null) {
                    log.info("连接服务端中...");
                    connectProxy();
                    return;
                }
                if (!serverChannel.isOpen()) {
                    log.info("重连服务端中...");
                    connectProxy();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, 3, 3, TimeUnit.SECONDS);
        // 发送心跳
        heartbeatExecutor.scheduleAtFixedRate(() -> {
            if (serverChannel != null && serverChannel.isOpen()) {
                log.info("发送心跳...");
                Msg.sendMsg(Msg.PROXY_CONNECT, new Msg.Header(clientId, 0, null, verifyKey, null, null), null, serverChannel);
            }
        }, 0, 30, TimeUnit.SECONDS);
    }

    public void connectProxy() {
        closeAll();
        new SocketCreateUtil().openClientProxy(serverIp, serverPort, new TcpConsumer<Msg>(
            ctx -> {
                serverChannel = ctx.channel();
                Msg.sendMsg(Msg.PROXY_CONNECT, new Msg.Header(clientId, 0, null, verifyKey, null, null), null, serverChannel);
                log.info("代理服务连接成功...");
            },
            (ctx, msg) -> {
            switch (msg.getType()) {
                // 连接服务端代理失败（服务端代理 -> 客户端代理，用于客户端被禁用、客户端正在使用、授权失败）
                case Msg.PROXY_CONNECT_ERROR:
                    log.info(" 连接服务端代理异常 {} ", new String(msg.getData(), StandardCharsets.UTF_8));
                    reconnectExecutor.shutdown();
                    serverChannel.close();
                    break;
                // 处理访客连接（服务端代理 -> 客户端代理，用于创建服务端访客服务，并且保存访客id）
                case Msg.PROXY_REQUEST:
                    this.proxyRequest(msg);
                    break;
                case Msg.VISITOR_SERVER_CLOSE:
                    log.info("接收到关闭事件");
                    handleServerVisitorClose(new String(msg.getData()));
                default:
                    break;
            }
        }, null, null).toPipeline());
    }
}
